<?php
/**
 * Created by .
 * User: luogan
 * Date: 2022/1/13
 */

namespace app\admin\service;

use app\exception\ErrorException;
use app\exception\TokenException;
use Firebase\JWT\JWT;
use Firebase\JWT\SignatureInvalidException;
use Firebase\JWT\BeforeValidException;
use Firebase\JWT\ExpiredException;
use think\facade\Cache;
class TokenService
{


    protected $config =[];

    /**
     * @var string
     */
    private $auth_prefix = 'admin_token:';

    public function __construct()
    {
        $this->config = config('token');
    }

    /**
     * 生成Token(单点登录)
     * @param $payload
     * @return string
     */
    public function signToken($payload)
    {

        $token = [
            "iss"  => $this->config['iss'],
            "aud"  => $this->config['aud'],
            "iat"  => $this->config['iat'],
            "nbf"  => $this->config['nbf'],
            "exp"  => $this->config['exp'],
            "data" => $payload
        ];

        $jwtStr = JWT::encode($token, $this->config['key'], $this->config['alg']);  //根据参数生成了 token

        try {
            Cache::store('redis')->set($this->auth_prefix.$payload['admin_id'],$jwtStr,3600*3);
        }catch(\RedisException $e){
            throw new ErrorException('redis连接错误');
        }


        /* 暂时使用一个token
        $access_token = $token;
        $access_token['scopes'] = 'role_access'; //token标识，请求接口的token
        $access_token['exp'] = time()+7200; //access_token过期时间,这里设置2个小时

        $refresh_token = $token;
        $refresh_token['scopes'] = 'role_refresh'; //token标识，刷新access_token
        $refresh_token['exp'] = time()+(86400 * 30); //access_token过期时间,这里设置30天

        $jsonList = [
            'access_token'=>JWT::encode($access_token,$config['key']),
            'refresh_token'=>JWT::encode($refresh_token,$config['key']),
            'token_type'=>'bearer' //token_type：表示令牌类型，该值大小写不敏感，这里用bearer
        ];*/

        return $jwtStr;
    }


    /**
     * 验证Token
     * @return mixed
     * @throws TokenException
     */
    public function checkToken()
    {
        $token = $this->getToken();

        try {
            JWT::$leeway = 60;//当前时间减去60，把时间留点余地
            $decoded = JWT::decode($token, $this->config['key'], [$this->config['alg']]);
            $arr = (array)$decoded;
            $payload = (array)$arr['data'];

        } catch (SignatureInvalidException $e) { //签名不正确

            throw new TokenException('登录签名不正确');

        } catch (BeforeValidException $e) { // 签名在某个时间点之后才能用

            throw new TokenException('登录失效,请重新登录');

        } catch (ExpiredException $e) { // token过期

            throw new TokenException('登录失效');

        } catch (\Exception $e) { //其他错误

            throw new TokenException('登录未知错误');
        }

        //判断redis中的token
        try {
            $redisToken = Cache::store('redis')->get($this->auth_prefix.$payload['admin_id']);
        }catch(\RedisException $e){
            throw new ErrorException('redis连接错误');
        }

        if ($redisToken == null){
            throw new TokenException('已退登录,请重新登录');
        }

        if (!hash_equals($token,$redisToken)){
            throw new TokenException('登录失效,请重新登录');
        }
        return $payload;
    }

    /**
     * 获取Token
     * @return false|string
     * @throws TokenException
     */
    public function getToken()
    {
        $tokenStr = request()->header('authorization');

        if (empty($tokenStr)){
            throw new TokenException('请先登录！');
        }
        //转译字符串
        $tokenStr = urldecode($tokenStr);

        //如果token带有Bearer类型标识
        if (strtolower(substr($tokenStr,0,6)) == $this->config['type']){
            $tokenStr = substr($tokenStr, 7);
        }

        return $tokenStr;
    }

    /**
     * 删除redis token
     * @param $adminId
     * @return bool
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function delToken($adminId)
    {
        $result = Cache::store('redis')->delete($this->auth_prefix.$adminId);

        return $result;
    }
}